With the File Provider Model, not only are the operations on file data (contents) generalized, but also the operations on the file system itself, such as navigation between directories and files, creating and deleting files and directories, etc. The File Provider Model allows you to work with OPC UA file system in the same way as with the physical file system, and other file systems for which the file providers are available (such as file providers for embedded resources) from Microsoft or other parties.
See Microsoft documentation, File Providers in ASP.NET Core, for fundamentals of the file provider model. Microsoft's file provider model is read-only (does not allow any changes to files or directories), and lacks some features. OPC Data Client builds on top of the Microsoft's file provider model, adding the missing features, and an ability to modify the files and directories.
For accessing the actual file data, the File Provider uses the Stream abstraction, analogous to API Level 2++, differing only in the methods that you use to obtain the data streams. Once your code has obtained the stream object for a particular OPC UA file, it can work with it as with any other data stream.
This article describes API level 3 for OPC UA file transfer; for an overview of API levels, see File Provider Model in OPC UA.
You may also want to look at OPC UA File Transfers internals in order to understand a bit of what is happening "under the hood".
Objects in the file provider model are accessed through interfaces. There are three main groups of interfaces. First one (a usual starting point) is for access to the file system as a whole - called "file provider". You can obtain "file info" interfaces and "directory contents" interfaces from the file provider, they are the second and the third group. "File info" is for access to individual files, and (to a limited extent) an information about individual directories. "Directory contents" represents the directory, together with its files and sub-directories.
For access to the file system as a whole, or its part, the interfaces below are used; we will refer to them as "file provider" interfaces (and they are implemented by "file provider" objects):
For access to files and file data, the interfaces below are used; we will refer to them as "file info" interfaces (and they are implemented by "file info" objects):
For access to directories in the file system, the interfaces: below are used; we will refer to them as "directory contents" interfaces (and they are implemented by "directory contents" objects):
Since the interfaces with the digit "2" at the end of their name are simply improvements over the interfaces without the "2", in most cases you can simply use the interfaces with the "2" suffix instead. In theory, you could also extend this reasoning further, and since the interfaces with "Writable" in their name only add writable file system functionality to the interfaces with "2" at the end of their name, you could just use the interfaces with "Writable" in their name wherever possible, and not bother with the other interfaces. This approach would work, but we recommend that you only use the interfaces with "Writable" in their name only if their additional methods are really needed, and do not "pull in" the unnecessary functionality in this way. In addition, there are slight differences in behavior for non-existent objects, etc.
If you need to make OPC UA File Transfer operations and do not want to write code, or if you just need to experiment with it, you can use the OpcCmd Utility, which has full support for OPC UA File Transfer. For more information, see Using OpcCmd Utility for OPC UA File Transfer. What's particularly worth noting is that there commands available for OPC UA File Transfer in the OpcCmd utility that are based on the file provider model, and therefore the information about how the file provider model works in the code API is also relevant for and usable with the OpcCmd utility. For example, the "fileInfo" command in OpcCmd provides functionality that corresponds to the IWritableFileInfo Interface, and the "directoryContents" command provides functionality that corresponds to the IWritableDirectoryContents Interface.
Microsoft does not document the behavior of the interfaces used in the file provider model well (or not at all). This has consequences on anything that implements, extends or consumes these interfaces. We have tried to adopt an error behavior similar to what Microsoft does in their own file providers, by making experiments and carefully studying the available source code.
Many error conditions are indicated by throwing an IOException or an exception derived from it, however that is not the full story. The exceptions thrown by methods in the file provider model are (derived exception are allowed too, of course):
Contrary to common practice, accessing object properties can throw exceptions as well.
Note that operations that just obtain one of the file provider model interfaces do not fail. Instead of failing, they will return an interface that represents an "invalid" or "not found" object, and behaves appropriately. This is intentional, and in some cases, is the normal way of operation. For example, you can obtain a "directory contents" object for a directory that does not yet exist, and then call the Create Method to create the directory. Similarly, you can obtain a "file info" object for a file that does not yet exist, and then call the CreateWriteStream Method to create the file and start writing into it.
The file provider model does not have a good way of reporting directory enumeration failures (the objects that could not be enumerate will simply be missing from the sequence), and some property-get failures.
How the objects in the file provider model are obtained depends on their type. Our concern here are only the objects that implement the file provider model for OPC UA File Transfer, so that's what we are going to describe below.
If you already have any of the "file provider" interfaces, the "file info" and "directory contents" interfaces can be obtained from them. In addition, OPC Data Client allows you to obtain the "file info" and "directory contents" objects directly, without having to start with a "file provider" object. This may come handy (and is, in fact, necessary if you are dealing with OPC UA files that are not part of a file system), but the downside is that you may not be able to reach "outside" of the capabilities of the object you started with. For example, if you start with the "file info" object, then just by using its interfaces, you cannot make any operations on its containing directory. If you, however, start with the "file provider", you will always have a way of reaching anything that is in or under the directory where the file provider is "rooted".
In order to directly obtain file provider objects, you use methods in the IEasyUAFileTransferExtension Class. They are:
All above methods reside "outside" the file provider model, i.e. they provide entry points into the model. At least one of them must eventually be called, if you want to start the objects in the file provider model. Note that there are no methods for obtaining the interfaces without "2" at the end of their name; this is not a problem, as the interfaces with the "2" suffix provide all functionality of their base interfaces, and some more.
Indirectly, further file provider objects can then be obtained by various ways; some of them are listed below.
All above methods reside "inside" the file provider model, and are the same regardless of the type of the file provider (there is nothing OPC UA-specific about them, and OPC UA concepts such as node Ids are no longer "visible").
The code showing how the file provider objects can be obtained can be seen in all code examples in this article.
Information about a file or directory can be obtained by getting properties of the corresponding "file info" object.
The example below shows how to get OPC UA file properties (such as its size or last modified time), using the file provider model.
The "directory contents" objects, by themselves, provide a sequence (enumerable) of "file info" objects, representing the files and sub-directories contained in the directory.
The example below shows how to browse for OPC UA files and directories, using the file provider model.
Other (extension) methods in this group, possibly not used in the above examples, include:
The file provider model uses .NET streams (Stream Class) for file reading, writing and positioning. In part, the information in the OPC UA File Streams article also applies.
In order to read data from an existing file in the file provider model, start by calling the CreateReadStream Method on the "file info" object. This method returns a stream object that can subsequently be used to access the file data for reading. Eventually, the stream object should be disposed of using the IDisposable.Dispose Method.
The example below shows how to read different sections from an OPC UA file, using the file provider model.
The CreateStreamReader Method creates a "text reader" for reading text from a file; optionally, you can specify the encoding to be used.
The example below shows how to open a file stream for reading, and read its content using a text reader object, using OPC UA file provider model.
In some cases, reading a file part by part is not needed, and you want to read the full file contents into memory. There extension methods like ReadAllBytes Method or ReadAllText Method that allow you to do that easily.
The example below shows how to read the full contents of an OPC UA file at once, using the file provider model.
In order to write data to a file in the file provider model, start by calling the CreateWriteStream Method on the "file info" object. There are also extension methods (see CreateWriteStream Method) with different structure of input arguments. All these methods return a stream object that can subsequently be used to access the file data for writing or reading. Eventually, the stream object should be disposed of using the IDisposable.Dispose Method
The example below shows how to write data into a section of an OPC UA file, using the file provider model.
In some cases, writing a file part by part is not needed, and you want to write the full file contents from what you already have in memory. There are extension methods like WriteAllBytes Method or WriteAllText Method that allow you to do that easily.
The example below shows how to write the full contents of an OPC UA file at once, using the file provider model.
Other (extension) methods in this group, possibly not used in the above examples, include:
You can load the contents of any OPC UA file (in fact, any file in the file provider model) from a normal (operating system) file, using the LoadFromOSFile Method.
The contents of any OPC UA file (in fact, any file in the file provider model) can be saved to a normal (operating system) file using the SaveToOSFile Method.
Various methods on the "directory contents" and "file info" objects, and extensions methods on them, provide functionality for creating, deleting, copying and moving directories and files. The examples that follow show how the basic file system manipulations can be programmed.
The example below shows how to create and delete OPC UA files, using the file provider model.
The example below shows how to create and delete OPC UA directories, using the file provider model.
The example below shows how to copy an OPC UA file, using the file provider model.
Other (extension) methods in this group, possibly not used in the above examples, include:
You can load the contents of any OPC UA directory (in fact, any directory in the file provider model) from a physical directory (this includes all files and sub-directories, recursively), using the LoadFromOSDirectory Method.
The contents of any OPC UA directory (in fact, any directory in the file provider model), including all files and sub-directories, can be saved to a physical directory using the SaveToOSDirectory Method.